// 
//  
//  Copyright (C) 2004-2009  Autodesk, Inc.
//  
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of version 2.1 of the GNU Lesser
//  General Public License as published by the Free Software Foundation.
//  
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//  
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//  

#include "stdafx.h"
#include <FdoCommonStringUtil.h>
#include <FdoCommonMiscUtil.h>

#include "MapInfoConnection.h"
#include "MapInfoOptimizedAggregateReader.h"
#include "MapInfoClassDefinition.h"

MapInfoOptimizedAggregateReader::MapInfoOptimizedAggregateReader(MapInfoConnection* conn, FdoFeatureClass* originalClass, aggr_list *selAggrList) :
    m_ReaderIndex(-1),
    m_SelAggrList(selAggrList),
    m_Count(0)
{
    MapInfoClassDefinition* mapinfoClassDef = conn->GetMapInfoClassDefinition(originalClass);

    for ( size_t i = 0; i < m_SelAggrList->size() && (m_Extents == NULL); i++ )
    { 
        AggregateElement    *id = m_SelAggrList->at(i);
        if ( id->type != FdoPropertyType_GeometricProperty )
            continue;

        double dXMin(0.0), dYMin(0.0), dZMin(0.0), dXMax(0.0), dYMax(0.0), dZMax(0.0);
        if (!mapinfoClassDef->GetExtents(dXMin, dYMin, dXMax, dYMax))
            m_Extents = NULL;
        else
        {
            // Build an extents geometry out of the spatial extents;
            // We ignore M dimensionality since FdoIEnvelope only exposes XYZ envelopes
            FdoPtr<FdoFgfGeometryFactory> gf = FdoFgfGeometryFactory::GetInstance();
            FdoPtr<FdoGeometricPropertyDefinition> geomProp = originalClass->GetGeometryProperty();
            FdoBoolean hasElevation = geomProp->GetHasElevation();
            FdoDimensionality geomDim = (FdoDimensionality)
                (hasElevation ? FdoDimensionality_XY | FdoDimensionality_Z : FdoDimensionality_XY);

            // Copy the extent values to an array of doubles
            FdoInt32 i=0;
            FdoDouble ordinates[15];
            ordinates[i++] = dXMin;
            ordinates[i++] = dYMin;
            if (hasElevation)
                ordinates[i++] = dZMin;

            ordinates[i++] = dXMax;
            ordinates[i++] = dYMin;
            if (hasElevation)
                ordinates[i++] = dZMax;

            ordinates[i++] = dXMax;
            ordinates[i++] = dYMax;
            if (hasElevation)
                ordinates[i++] = dZMax; 

            ordinates[i++] = dXMin;
            ordinates[i++] = dYMax;
            if (hasElevation)
                ordinates[i++] = dZMin; 

            ordinates[i++] = dXMin;
            ordinates[i++] = dYMin;
            if (hasElevation)
                ordinates[i++] = dZMin; 

            // Create a linear ring using the bounding box ordinates 
            FdoPtr<FdoILinearRing> linearRing = gf->CreateLinearRing(geomDim, i, ordinates);

            // Create a polygon geometry representing the extents from the linear ring
            m_Extents = gf->CreatePolygon(linearRing, NULL);
        }
    }

    // Do Count()
    for ( size_t i = 0; i < m_SelAggrList->size() && (m_Count == 0); i++ )
    {
        AggregateElement    *id = m_SelAggrList->at(i);
        if ( id->type != FdoPropertyType_DataProperty )
            continue;

        m_Count = mapinfoClassDef->GetFeatureCount();
     }
}

MapInfoOptimizedAggregateReader::~MapInfoOptimizedAggregateReader()
{
    // Clean up
    for ( size_t i = 0; i < m_SelAggrList->size(); i++ )
        delete m_SelAggrList->at(i);
    delete m_SelAggrList;
}

void MapInfoOptimizedAggregateReader::Dispose()
{
    delete this;
}

bool MapInfoOptimizedAggregateReader::ReadNext()
{
    m_ReaderIndex++;
    if (m_ReaderIndex==0)
        PopulatePropertyValueCache();

    return (m_ReaderIndex==0);
}

void MapInfoOptimizedAggregateReader::Close()
{
}

FdoInt32 MapInfoOptimizedAggregateReader::GetItemCount(void)
{
    return (FdoInt32)m_SelAggrList->size();  // always one property in result set
}

FdoString* MapInfoOptimizedAggregateReader::GetItemName(FdoInt32 i)
{
    if ( i > (FdoInt32)m_SelAggrList->size() )
        throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(FDO_1_INVALID_INPUT_ON_CLASS_FUNCTION),
                                                        L"MapInfoOptimizedAggregateReader::GetItemName",
                                                        L"index"));
    return m_SelAggrList->at(i)->name; 
}

FdoInt32 MapInfoOptimizedAggregateReader::GetItemIndex(FdoString* itemName)  // throw exception if name is invalid
{
    bool        found = false;
    FdoInt32    index;

    for ( FdoInt32 i = 0; i < (FdoInt32)m_SelAggrList->size() && !found; i++ )
    {
        found = ( wcscmp(m_SelAggrList->at(i)->name, itemName ) == 0 );
        index = i;
    }

    if ( !found )
        throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(FDO_1_INVALID_INPUT_ON_CLASS_FUNCTION),
                                                        L"MapInfoOptimizedAggregateReader::GetItemIndex",
                                                        L"itemName"));
    return index;
}

FdoPropertyType MapInfoOptimizedAggregateReader::GetItemType(FdoInt32 i)
{
    return m_SelAggrList->at(i)->type;
}

FdoDataType MapInfoOptimizedAggregateReader::GetItemDataType(FdoInt32 i)
{
    if ( i > (FdoInt32)m_SelAggrList->size() )
        throw FdoException::Create(FdoException::NLSGetMessage(FDO_NLSID(FDO_1_INVALID_INPUT_ON_CLASS_FUNCTION),
                                                        L"MapInfoOptimizedAggregateReader::GetItemDataType",
                                                        L"index"));

    // So any call to this function will fail if not for Count()
    if ( m_SelAggrList->at(i)->type != FdoPropertyType_DataProperty )
        throw FdoException::Create (FdoException::NLSGetMessage(FDO_NLSID(FDO_89_UNSUPPORTED_FUNCTION), L"COUNT"));

    return FdoDataType_Int64;
}

// Retrieve data values; these are guaranteed to only be called once per row of data:
void MapInfoOptimizedAggregateReader::GetGeometryForCache(FdoIdentifier *itemName, FdoByteArray **byteArray, bool *bIsNull)
{
    FdoPtr<FdoFgfGeometryFactory> gf = FdoFgfGeometryFactory::GetInstance();
    FdoByteArray* geomFGF = !m_Extents ? NULL : gf->GetFgf(m_Extents);

    if (bIsNull)
        *bIsNull = !m_Extents;
    if (byteArray)
        *byteArray = geomFGF;
}

void MapInfoOptimizedAggregateReader::GetInt64ForCache( FdoIdentifier *itemName, FdoInt64 *int64Value, bool *bIsNull )
{
    if (bIsNull)
        *bIsNull = false;
    if (int64Value)
        *int64Value = m_Count;
}

// Retrieve information about the row of items:
bool MapInfoOptimizedAggregateReader::ReaderHasData(void)
{
    return (m_ReaderIndex==0);  // return false IFF before first row or after last row
}

